// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stdint.h>

#include <vector>

#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/mock_storage_client.h"
#include "net/base/url_util.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/quota/storage_monitor.h"
#include "storage/browser/quota/storage_observer.h"
#include "testing/gtest/include/gtest/gtest.h"

using storage::HostStorageObservers;
using storage::kQuotaErrorNotSupported;
using storage::kQuotaStatusOk;
using storage::kStorageTypePersistent;
using storage::kStorageTypeTemporary;
using storage::QuotaClient;
using storage::QuotaManager;
using storage::QuotaStatusCode;
using storage::SpecialStoragePolicy;
using storage::StorageMonitor;
using storage::StorageObserver;
using storage::StorageObserverList;
using storage::StorageType;
using storage::StorageTypeObservers;

namespace content {

namespace {

    const char kDefaultOrigin[] = "http://www.foo.com/";
    const char kAlternativeOrigin[] = "http://www.bar.com/";

    class MockObserver : public StorageObserver {
    public:
        const StorageObserver::Event& LastEvent() const
        {
            CHECK(!events_.empty());
            return events_.back();
        }

        int EventCount() const
        {
            return events_.size();
        }

        // StorageObserver implementation:
        void OnStorageEvent(const StorageObserver::Event& event) override
        {
            events_.push_back(event);
        }

    private:
        std::vector<StorageObserver::Event> events_;
    };

    // A mock quota manager for overriding GetUsageAndQuotaForWebApps().
    class UsageMockQuotaManager : public QuotaManager {
    public:
        UsageMockQuotaManager(SpecialStoragePolicy* special_storage_policy)
            : QuotaManager(false,
                base::FilePath(),
                base::ThreadTaskRunnerHandle::Get().get(),
                base::ThreadTaskRunnerHandle::Get().get(),
                special_storage_policy)
            , callback_usage_(0)
            , callback_quota_(0)
            , callback_status_(kQuotaStatusOk)
            , initialized_(false)
        {
        }

        void SetCallbackParams(int64_t usage, int64_t quota, QuotaStatusCode status)
        {
            initialized_ = true;
            callback_quota_ = quota;
            callback_usage_ = usage;
            callback_status_ = status;
        }

        void InvokeCallback()
        {
            delayed_callback_.Run(callback_status_, callback_usage_, callback_quota_);
        }

        void GetUsageAndQuotaForWebApps(
            const GURL& origin,
            StorageType type,
            const GetUsageAndQuotaCallback& callback) override
        {
            if (initialized_)
                callback.Run(callback_status_, callback_usage_, callback_quota_);
            else
                delayed_callback_ = callback;
        }

    protected:
        ~UsageMockQuotaManager() override { }

    private:
        int64_t callback_usage_;
        int64_t callback_quota_;
        QuotaStatusCode callback_status_;
        bool initialized_;
        GetUsageAndQuotaCallback delayed_callback_;
    };

} // namespace

class StorageMonitorTestBase : public testing::Test {
protected:
    void DispatchPendingEvents(StorageObserverList& observer_list)
    {
        observer_list.DispatchPendingEvent();
    }

    const StorageObserver::Event* GetPendingEvent(
        const StorageObserverList& observer_list)
    {
        return observer_list.notification_timer_.IsRunning()
            ? &observer_list.pending_event_
            : NULL;
    }

    const StorageObserver::Event* GetPendingEvent(
        const HostStorageObservers& host_observers)
    {
        return GetPendingEvent(host_observers.observers_);
    }

    int GetRequiredUpdatesCount(const StorageObserverList& observer_list)
    {
        int count = 0;
        for (StorageObserverList::StorageObserverStateMap::const_iterator it = observer_list.observers_.begin();
             it != observer_list.observers_.end(); ++it) {
            if (it->second.requires_update)
                ++count;
        }

        return count;
    }

    int GetRequiredUpdatesCount(const HostStorageObservers& host_observers)
    {
        return GetRequiredUpdatesCount(host_observers.observers_);
    }

    void SetLastNotificationTime(StorageObserverList& observer_list,
        StorageObserver* observer)
    {
        ASSERT_TRUE(observer_list.observers_.find(observer) != observer_list.observers_.end());

        StorageObserverList::ObserverState& state = observer_list.observers_[observer];
        state.last_notification_time = base::TimeTicks::Now() - state.rate;
    }

    void SetLastNotificationTime(HostStorageObservers& host_observers,
        StorageObserver* observer)
    {
        SetLastNotificationTime(host_observers.observers_, observer);
    }

    int GetObserverCount(const HostStorageObservers& host_observers)
    {
        return host_observers.observers_.ObserverCount();
    }
};

class StorageTestWithManagerBase : public StorageMonitorTestBase {
public:
    void SetUp() override
    {
        storage_policy_ = new MockSpecialStoragePolicy();
        quota_manager_ = new UsageMockQuotaManager(storage_policy_.get());
    }

    void TearDown() override
    {
        // This ensures the quota manager is destroyed correctly.
        quota_manager_ = NULL;
        base::RunLoop().RunUntilIdle();
    }

protected:
    base::MessageLoop message_loop_;
    scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
    scoped_refptr<UsageMockQuotaManager> quota_manager_;
};

// Tests for StorageObserverList:

typedef StorageMonitorTestBase StorageObserverListTest;

// Test dispatching events to one observer.
TEST_F(StorageObserverListTest, DispatchEventToSingleObserver)
{
    // A message loop is required as StorageObserverList may schedule jobs.
    base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);

    StorageObserver::MonitorParams params(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    MockObserver mock_observer;
    StorageObserverList observer_list;
    observer_list.AddObserver(&mock_observer, params);

    StorageObserver::Event event;
    event.filter = params.filter;

    // Verify that the first event is dispatched immediately.
    event.quota = 1;
    event.usage = 1;
    observer_list.OnStorageChange(event);
    EXPECT_EQ(1, mock_observer.EventCount());
    EXPECT_EQ(event, mock_observer.LastEvent());
    EXPECT_EQ(NULL, GetPendingEvent(observer_list));
    EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));

    // Verify that the next event is pending.
    event.quota = 2;
    event.usage = 2;
    observer_list.OnStorageChange(event);
    EXPECT_EQ(1, mock_observer.EventCount());
    ASSERT_TRUE(GetPendingEvent(observer_list));
    EXPECT_EQ(event, *GetPendingEvent(observer_list));
    EXPECT_EQ(1, GetRequiredUpdatesCount(observer_list));

    // Fake the last notification time so that an event will be dispatched.
    SetLastNotificationTime(observer_list, &mock_observer);
    event.quota = 3;
    event.usage = 3;
    observer_list.OnStorageChange(event);
    EXPECT_EQ(2, mock_observer.EventCount());
    EXPECT_EQ(event, mock_observer.LastEvent());
    EXPECT_EQ(NULL, GetPendingEvent(observer_list));
    EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));

    // Remove the observer.
    event.quota = 4;
    event.usage = 4;
    observer_list.RemoveObserver(&mock_observer);
    observer_list.OnStorageChange(event);
    EXPECT_EQ(2, mock_observer.EventCount());
    EXPECT_EQ(NULL, GetPendingEvent(observer_list));
}

// Test dispatching events to multiple observers.
TEST_F(StorageObserverListTest, DispatchEventToMultipleObservers)
{
    // A message loop is required as StorageObserverList may schedule jobs.
    base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);

    MockObserver mock_observer1;
    MockObserver mock_observer2;
    StorageObserverList observer_list;
    StorageObserver::Filter filter(kStorageTypePersistent,
        GURL(kDefaultOrigin));
    observer_list.AddObserver(
        &mock_observer1,
        StorageObserver::MonitorParams(
            filter, base::TimeDelta::FromHours(1), false));
    observer_list.AddObserver(
        &mock_observer2,
        StorageObserver::MonitorParams(
            filter, base::TimeDelta::FromHours(2), false));

    StorageObserver::Event event;
    event.filter = filter;

    // Verify that the first event is dispatched immediately.
    event.quota = 1;
    event.usage = 1;
    observer_list.OnStorageChange(event);
    EXPECT_EQ(1, mock_observer1.EventCount());
    EXPECT_EQ(1, mock_observer2.EventCount());
    EXPECT_EQ(event, mock_observer1.LastEvent());
    EXPECT_EQ(event, mock_observer2.LastEvent());
    EXPECT_EQ(NULL, GetPendingEvent(observer_list));
    EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));

    // Fake the last notification time so that observer1 will receive the next
    // event, but it will be pending for observer2.
    SetLastNotificationTime(observer_list, &mock_observer1);
    event.quota = 2;
    event.usage = 2;
    observer_list.OnStorageChange(event);
    EXPECT_EQ(2, mock_observer1.EventCount());
    EXPECT_EQ(1, mock_observer2.EventCount());
    EXPECT_EQ(event, mock_observer1.LastEvent());
    ASSERT_TRUE(GetPendingEvent(observer_list));
    EXPECT_EQ(event, *GetPendingEvent(observer_list));
    EXPECT_EQ(1, GetRequiredUpdatesCount(observer_list));

    // Now dispatch the pending event to observer2.
    SetLastNotificationTime(observer_list, &mock_observer2);
    DispatchPendingEvents(observer_list);
    EXPECT_EQ(2, mock_observer1.EventCount());
    EXPECT_EQ(2, mock_observer2.EventCount());
    EXPECT_EQ(event, mock_observer1.LastEvent());
    EXPECT_EQ(event, mock_observer2.LastEvent());
    EXPECT_EQ(NULL, GetPendingEvent(observer_list));
    EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));
}

// Ensure that the |origin| field in events match the origin specified by the
// observer on registration.
TEST_F(StorageObserverListTest, ReplaceEventOrigin)
{
    StorageObserver::MonitorParams params(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    MockObserver mock_observer;
    StorageObserverList observer_list;
    observer_list.AddObserver(&mock_observer, params);

    StorageObserver::Event dispatched_event;
    dispatched_event.filter = params.filter;
    dispatched_event.filter.origin = GURL("https://www.foo.com/bar");
    observer_list.OnStorageChange(dispatched_event);

    EXPECT_EQ(params.filter.origin, mock_observer.LastEvent().filter.origin);
}

// Tests for HostStorageObservers:

typedef StorageTestWithManagerBase HostStorageObserversTest;

// Verify that HostStorageObservers is initialized after the first usage change.
TEST_F(HostStorageObserversTest, InitializeOnUsageChange)
{
    StorageObserver::MonitorParams params(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    const int64_t kUsage = 324554;
    const int64_t kQuota = 234354354;
    quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);

    MockObserver mock_observer;
    HostStorageObservers host_observers(quota_manager_.get());
    host_observers.AddObserver(&mock_observer, params);

    // Verify that HostStorageObservers dispatches the first event correctly.
    StorageObserver::Event expected_event(params.filter, kUsage, kQuota);
    host_observers.NotifyUsageChange(params.filter, 87324);
    EXPECT_EQ(1, mock_observer.EventCount());
    EXPECT_EQ(expected_event, mock_observer.LastEvent());
    EXPECT_TRUE(host_observers.is_initialized());

    // Verify that HostStorageObservers handles subsequent usage changes
    // correctly.
    const int64_t kDelta = 2345;
    expected_event.usage += kDelta;
    SetLastNotificationTime(host_observers, &mock_observer);
    host_observers.NotifyUsageChange(params.filter, kDelta);
    EXPECT_EQ(2, mock_observer.EventCount());
    EXPECT_EQ(expected_event, mock_observer.LastEvent());
}

// Verify that HostStorageObservers is initialized after the adding the first
// observer that elected to receive the initial state.
TEST_F(HostStorageObserversTest, InitializeOnObserver)
{
    const int64_t kUsage = 74387;
    const int64_t kQuota = 92834743;
    quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
    HostStorageObservers host_observers(quota_manager_.get());

    // |host_observers| should not be initialized after the first observer is
    // added because it did not elect to receive the initial state.
    StorageObserver::MonitorParams params(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    MockObserver mock_observer1;
    host_observers.AddObserver(&mock_observer1, params);
    EXPECT_FALSE(host_observers.is_initialized());
    EXPECT_EQ(0, mock_observer1.EventCount());

    // |host_observers| should be initialized after the second observer is
    // added.
    MockObserver mock_observer2;
    params.dispatch_initial_state = true;
    host_observers.AddObserver(&mock_observer2, params);
    StorageObserver::Event expected_event(params.filter, kUsage, kQuota);
    EXPECT_EQ(0, mock_observer1.EventCount());
    EXPECT_EQ(1, mock_observer2.EventCount());
    EXPECT_EQ(expected_event, mock_observer2.LastEvent());
    EXPECT_TRUE(host_observers.is_initialized());
    EXPECT_EQ(NULL, GetPendingEvent(host_observers));
    EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));

    // Verify that both observers will receive events after a usage change.
    const int64_t kDelta = 2345;
    expected_event.usage += kDelta;
    SetLastNotificationTime(host_observers, &mock_observer2);
    host_observers.NotifyUsageChange(params.filter, kDelta);
    EXPECT_EQ(1, mock_observer1.EventCount());
    EXPECT_EQ(2, mock_observer2.EventCount());
    EXPECT_EQ(expected_event, mock_observer1.LastEvent());
    EXPECT_EQ(expected_event, mock_observer2.LastEvent());
    EXPECT_EQ(NULL, GetPendingEvent(host_observers));
    EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));

    // Verify that the addition of a third observer only causes an event to be
    // dispatched to the new observer.
    MockObserver mock_observer3;
    params.dispatch_initial_state = true;
    host_observers.AddObserver(&mock_observer3, params);
    EXPECT_EQ(1, mock_observer1.EventCount());
    EXPECT_EQ(2, mock_observer2.EventCount());
    EXPECT_EQ(1, mock_observer3.EventCount());
    EXPECT_EQ(expected_event, mock_observer3.LastEvent());
}

// Verify that negative usage and quota is changed to zero.
TEST_F(HostStorageObserversTest, NegativeUsageAndQuota)
{
    StorageObserver::MonitorParams params(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    const int64_t kUsage = -324554;
    const int64_t kQuota = -234354354;
    quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);

    MockObserver mock_observer;
    HostStorageObservers host_observers(quota_manager_.get());
    host_observers.AddObserver(&mock_observer, params);

    StorageObserver::Event expected_event(params.filter, 0, 0);
    host_observers.NotifyUsageChange(params.filter, -87324);
    EXPECT_EQ(expected_event, mock_observer.LastEvent());
}

// Verify that HostStorageObservers can recover from a bad initialization.
TEST_F(HostStorageObserversTest, RecoverFromBadUsageInit)
{
    StorageObserver::MonitorParams params(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    MockObserver mock_observer;
    HostStorageObservers host_observers(quota_manager_.get());
    host_observers.AddObserver(&mock_observer, params);

    // Set up the quota manager to return an error status.
    const int64_t kUsage = 6656;
    const int64_t kQuota = 99585556;
    quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaErrorNotSupported);

    // Verify that |host_observers| is not initialized and an event has not been
    // dispatched.
    host_observers.NotifyUsageChange(params.filter, 9438);
    EXPECT_EQ(0, mock_observer.EventCount());
    EXPECT_FALSE(host_observers.is_initialized());
    EXPECT_EQ(NULL, GetPendingEvent(host_observers));
    EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));

    // Now ensure that quota manager returns a good status.
    quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
    host_observers.NotifyUsageChange(params.filter, 9048543);
    StorageObserver::Event expected_event(params.filter, kUsage, kQuota);
    EXPECT_EQ(1, mock_observer.EventCount());
    EXPECT_EQ(expected_event, mock_observer.LastEvent());
    EXPECT_TRUE(host_observers.is_initialized());
}

// Verify that HostStorageObservers handle initialization of the cached usage
// and quota correctly.
TEST_F(HostStorageObserversTest, AsyncInitialization)
{
    StorageObserver::MonitorParams params(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    MockObserver mock_observer;
    HostStorageObservers host_observers(quota_manager_.get());
    host_observers.AddObserver(&mock_observer, params);

    // Trigger initialization. Leave the mock quota manager uninitialized so that
    // the callback is not invoked.
    host_observers.NotifyUsageChange(params.filter, 7645);
    EXPECT_EQ(0, mock_observer.EventCount());
    EXPECT_FALSE(host_observers.is_initialized());
    EXPECT_EQ(NULL, GetPendingEvent(host_observers));
    EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));

    // Simulate notifying |host_observers| of a usage change before initialization
    // is complete.
    const int64_t kUsage = 6656;
    const int64_t kQuota = 99585556;
    const int64_t kDelta = 327643;
    host_observers.NotifyUsageChange(params.filter, kDelta);
    EXPECT_EQ(0, mock_observer.EventCount());
    EXPECT_FALSE(host_observers.is_initialized());
    EXPECT_EQ(NULL, GetPendingEvent(host_observers));
    EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));

    // Simulate an asynchronous callback from QuotaManager.
    quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
    quota_manager_->InvokeCallback();
    StorageObserver::Event expected_event(params.filter, kUsage + kDelta, kQuota);
    EXPECT_EQ(1, mock_observer.EventCount());
    EXPECT_EQ(expected_event, mock_observer.LastEvent());
    EXPECT_TRUE(host_observers.is_initialized());
    EXPECT_EQ(NULL, GetPendingEvent(host_observers));
    EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));
}

// Tests for StorageTypeObservers:

typedef StorageTestWithManagerBase StorageTypeObserversTest;

// Test adding and removing observers.
TEST_F(StorageTypeObserversTest, AddRemoveObservers)
{
    StorageTypeObservers type_observers(quota_manager_.get());

    StorageObserver::MonitorParams params1(kStorageTypePersistent,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    StorageObserver::MonitorParams params2(kStorageTypePersistent,
        GURL(kAlternativeOrigin),
        base::TimeDelta::FromHours(1),
        false);
    std::string host1 = net::GetHostOrSpecFromURL(params1.filter.origin);
    std::string host2 = net::GetHostOrSpecFromURL(params2.filter.origin);

    MockObserver mock_observer1;
    MockObserver mock_observer2;
    MockObserver mock_observer3;
    type_observers.AddObserver(&mock_observer1, params1);
    type_observers.AddObserver(&mock_observer2, params1);

    type_observers.AddObserver(&mock_observer1, params2);
    type_observers.AddObserver(&mock_observer2, params2);
    type_observers.AddObserver(&mock_observer3, params2);

    // Verify that the observers have been removed correctly.
    ASSERT_TRUE(type_observers.GetHostObservers(host1));
    ASSERT_TRUE(type_observers.GetHostObservers(host2));
    EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host1)));
    EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2)));

    // Remove an observer for a specific filter.
    type_observers.RemoveObserverForFilter(&mock_observer1, params1.filter);
    ASSERT_TRUE(type_observers.GetHostObservers(host1));
    ASSERT_TRUE(type_observers.GetHostObservers(host2));
    EXPECT_EQ(1, GetObserverCount(*type_observers.GetHostObservers(host1)));
    EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2)));

    // Remove all instances of an observer.
    type_observers.RemoveObserver(&mock_observer2);
    ASSERT_TRUE(type_observers.GetHostObservers(host2));
    EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host2)));
    // Observers of host1 has been deleted as it is empty.
    EXPECT_FALSE(type_observers.GetHostObservers(host1));
}

// Tests for StorageMonitor:

class StorageMonitorTest : public StorageTestWithManagerBase {
public:
    StorageMonitorTest()
        : storage_monitor_(NULL)
        , params1_(kStorageTypeTemporary,
              GURL(kDefaultOrigin),
              base::TimeDelta::FromHours(1),
              false)
        , params2_(kStorageTypePersistent,
              GURL(kDefaultOrigin),
              base::TimeDelta::FromHours(1),
              false)
    {
    }

protected:
    void SetUp() override
    {
        StorageTestWithManagerBase::SetUp();

        storage_monitor_ = quota_manager_->storage_monitor_.get();
        host_ = net::GetHostOrSpecFromURL(params1_.filter.origin);

        storage_monitor_->AddObserver(&mock_observer1_, params1_);
        storage_monitor_->AddObserver(&mock_observer2_, params1_);

        storage_monitor_->AddObserver(&mock_observer1_, params2_);
        storage_monitor_->AddObserver(&mock_observer2_, params2_);
        storage_monitor_->AddObserver(&mock_observer3_, params2_);
    }

    int GetObserverCount(StorageType storage_type)
    {
        const StorageTypeObservers* type_observers = storage_monitor_->GetStorageTypeObservers(storage_type);
        return StorageMonitorTestBase::GetObserverCount(
            *type_observers->GetHostObservers(host_));
    }

    void CheckObserverCount(int expected_temporary, int expected_persistent)
    {
        ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
            kStorageTypeTemporary));
        ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
                                        kStorageTypeTemporary)
                        ->GetHostObservers(host_));
        EXPECT_EQ(expected_temporary, GetObserverCount(kStorageTypeTemporary));

        ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
            kStorageTypePersistent));
        ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
                                        kStorageTypePersistent)
                        ->GetHostObservers(host_));
        EXPECT_EQ(expected_persistent, GetObserverCount(kStorageTypePersistent));
    }

    StorageMonitor* storage_monitor_;
    StorageObserver::MonitorParams params1_;
    StorageObserver::MonitorParams params2_;
    MockObserver mock_observer1_;
    MockObserver mock_observer2_;
    MockObserver mock_observer3_;
    std::string host_;
};

// Test adding storage observers.
TEST_F(StorageMonitorTest, AddObservers)
{
    // Verify that the observers are added correctly.
    CheckObserverCount(2, 3);
}

// Test dispatching events to storage observers.
TEST_F(StorageMonitorTest, EventDispatch)
{
    // Verify dispatch of events.
    const int64_t kUsage = 5325;
    const int64_t kQuota = 903845;
    quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
    storage_monitor_->NotifyUsageChange(params1_.filter, 9048543);

    StorageObserver::Event expected_event(params1_.filter, kUsage, kQuota);
    EXPECT_EQ(1, mock_observer1_.EventCount());
    EXPECT_EQ(1, mock_observer2_.EventCount());
    EXPECT_EQ(0, mock_observer3_.EventCount());
    EXPECT_EQ(expected_event, mock_observer1_.LastEvent());
    EXPECT_EQ(expected_event, mock_observer2_.LastEvent());
}

// Test removing all instances of an observer.
TEST_F(StorageMonitorTest, RemoveObserver)
{
    storage_monitor_->RemoveObserver(&mock_observer1_);
    CheckObserverCount(1, 2);
}

// Test removing an observer for a specific filter.
TEST_F(StorageMonitorTest, RemoveObserverForFilter)
{
    storage_monitor_->RemoveObserverForFilter(&mock_observer1_, params2_.filter);
    CheckObserverCount(2, 2);
}

// Integration test for QuotaManager and StorageMonitor:

class StorageMonitorIntegrationTest : public testing::Test {
public:
    void SetUp() override
    {
        ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
        storage_policy_ = new MockSpecialStoragePolicy();
        quota_manager_ = new QuotaManager(
            false, data_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get(),
            base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get());

        client_ = new MockStorageClient(quota_manager_->proxy(),
            NULL,
            QuotaClient::kFileSystem,
            0);

        quota_manager_->proxy()->RegisterClient(client_);
    }

    void TearDown() override
    {
        // This ensures the quota manager is destroyed correctly.
        quota_manager_ = NULL;
        base::RunLoop().RunUntilIdle();
    }

protected:
    base::MessageLoop message_loop_;
    base::ScopedTempDir data_dir_;
    scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
    scoped_refptr<QuotaManager> quota_manager_;
    MockStorageClient* client_;
};

// This test simulates a usage change in a quota client and verifies that a
// storage observer will receive a storage event.
TEST_F(StorageMonitorIntegrationTest, NotifyUsageEvent)
{
    const StorageType kTestStorageType = kStorageTypePersistent;
    const int64_t kTestUsage = 234743;

    // Register the observer.
    StorageObserver::MonitorParams params(kTestStorageType,
        GURL(kDefaultOrigin),
        base::TimeDelta::FromHours(1),
        false);
    MockObserver mock_observer;
    quota_manager_->AddStorageObserver(&mock_observer, params);

    // Fire a usage change.
    client_->AddOriginAndNotify(GURL(kDefaultOrigin),
        kTestStorageType,
        kTestUsage);
    base::RunLoop().RunUntilIdle();

    // Verify that the observer receives it.
    ASSERT_EQ(1, mock_observer.EventCount());
    const StorageObserver::Event& event = mock_observer.LastEvent();
    EXPECT_EQ(params.filter, event.filter);
    EXPECT_EQ(kTestUsage, event.usage);
}

} // namespace content
